import * as THREE from "three"
import B3DMLoader from "./B3DMLoader"
import I3DMLoader from "./I3DMLoader"
import PNTSLoader from "./PNTSLoader"
class CMPTLoaderBase {
  fetchOptions: {}
  workingPath: string
  constructor() {
    ;(this.fetchOptions = {}), (this.workingPath = "")
  }
  load(e) {
    return fetch(e, this.fetchOptions)
      .then(t => {
        if (!t.ok) throw new Error(`Failed to load file "${e}" with status ${t.status} : ${t.statusText}`)
        return t.arrayBuffer()
      })
      .then(t => {
        if ("" === this.workingPath) {
          const t = e.split(/\\\//g)
          t.pop(), (this.workingPath = t.join("/"))
        }
        return this.parse(t)
      })
  }
  parse(e) {
    const t = new DataView(e),
      r = String.fromCharCode(t.getUint8(0)) + String.fromCharCode(t.getUint8(1)) + String.fromCharCode(t.getUint8(2)) + String.fromCharCode(t.getUint8(3))
    console.assert("cmpt" === r, 'CMPTLoader: The magic bytes equal "cmpt".')
    const s = t.getUint32(4, !0)
    console.assert(1 === s, 'CMPTLoader: The version listed in the header is "1".')
    const n = t.getUint32(8, !0)
    console.assert(n === e.byteLength, "CMPTLoader: The contents buffer length listed in the header matches the file.")
    const i = t.getUint32(12, !0),
      o = []
    let a = 16
    for (let t = 0; t < i; t++) {
      const t = new DataView(e, a, 12),
        r = String.fromCharCode(t.getUint8(0)) + String.fromCharCode(t.getUint8(1)) + String.fromCharCode(t.getUint8(2)) + String.fromCharCode(t.getUint8(3)),
        s = t.getUint32(4, !0),
        n = t.getUint32(8, !0),
        i = new Uint8Array(e, a, n)
      o.push({ type: r, buffer: i, version: s }), (a += n)
    }
    return { version: s, tiles: o }
  }
}
export default class CMPTLoader extends CMPTLoaderBase {
  manager: THREE.LoadingManager

  constructor(e = THREE.DefaultLoadingManager) {
    super(), (this.manager = e)
  }
  parse(e): any {
    const t = super.parse(e),
      r = this.manager,
      s = []
    for (const e in t.tiles) {
      const { type: n, buffer: i } = t.tiles[e]
      switch (n) {
        case "b3dm": {
          const e = i.slice(),
            t = new B3DMLoader(r)
          ;(t.workingPath = this.workingPath), (t.fetchOptions = this.fetchOptions)
          const n = t.parse(e.buffer)
          s.push(n)
          break
        }
        case "pnts": {
          const e = i.slice(),
            t = new PNTSLoader(r).parse(e.buffer),
            n = Promise.resolve(t)
          s.push(n)
          break
        }
        case "i3dm": {
          const e = i.slice(),
            t = new I3DMLoader(r)
          ;(t.workingPath = this.workingPath), (t.fetchOptions = this.fetchOptions)
          const n = t.parse(e.buffer)
          s.push(n)
          break
        }
      }
    }
    return Promise.all(s).then(e => {
      const t = new THREE.Group()
      return (
        e.forEach(e => {
          t.add(e.scene)
        }),
        { tiles: e, scene: t }
      )
    })
  }
}
